package baseFx.common.config.apollo;

import baseFx.common.Stream;
import baseFx.common.config.ConfigReader;
import baseFx.common.utils.StringUtils;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;

import java.util.*;

public class ApolloReader implements ConfigReader {
    private final Map<String, String> data = new HashMap<>();
    private final Set<String> loaded = new HashSet<>();

    public static void setEnv(Env env) {
        System.setProperty("env", env.name());
    }

    public ApolloReader() {

    }

    private void setConfig(String appId, String cluster, Map<Env, String> address) {
        ApolloConfig.setAppId(appId);
        ApolloConfig.setCluster(cluster);
        for (Env e : address.keySet()) {
            ApolloConfig.setAddress(e, address.get(e));
        }
    }

    public ApolloReader(String appId, String cluster, Map<Env, String> address) {
        setConfig(appId, cluster, address);
    }

    public ApolloReader(String appId, String cluster, Env env, String address) {
        Map<Env, String> map = new HashMap<>();
        map.put(env, address);
        setConfig(appId, cluster, map);
    }

    public ApolloReader(String appId) {
        this(appId, null);
    }

    public ApolloReader(String appId, Env env, String address) {
        this(appId, null, env, address);
    }

    public ApolloReader(String appId, String address) {
        this(appId, null, Env.UNKNOWN, address);
    }

    @Override
    public String read(String namespace, String name) {
        String full = getFullName(namespace, name);
        if (data.containsKey(full) == false) {
            if (loaded.contains(namespace)) {
                return null;
            }
            Config config = ConfigService.getConfig(namespace);
            Set<String> names = config.getPropertyNames();
            for (String item : names) {
                String value = config.getProperty(item, "");
                String key = getFullName(namespace, item);
                data.put(key, value);
            }
            loaded.add(namespace);
            config.addChangeListener(this::onChange);
        }
        return data.get(full);
    }

    @Override
    public List<String> getNames(String namespace) {
        if (StringUtils.hasValue(namespace)) {
            String find = namespace.toLowerCase() + "$";
            return Stream.create(data.keySet()).filter(i -> i.startsWith(find)).toList();
        } else {
            return new ArrayList<>();
        }
    }

    private void onChange(ConfigChangeEvent event) {
        for (String name : event.changedKeys()) {
            String key = getFullName(event.getNamespace(), name);
            ConfigChange change = event.getChange(name);
            switch (change.getChangeType()) {
                case MODIFIED:
                case ADDED:
                    data.put(key, change.getNewValue());
                    break;
                case DELETED:
                    data.remove(key);
                    break;
                default:
                    break;
            }
        }
    }

    private String getFullName(String namespace, String name) {
        return (namespace + "$" + name).toLowerCase();
    }
}
